﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace ClusteringWrraper
{
    public class LoggedInUser
    {
        
        #region VARIABLES
        private readonly string _token;
        private readonly int _myUid;
        private int _asyncCounter;
        private int _numOfFriends = 492;//My default... will be overwritten
        private bool _doneFillingConnections;
        private readonly Regex _mutualFriendsRegex = new Regex("{\"uid1\":\"(?<uid1>[0-9]*)\",\"uid2\":\"(?<uid2>[0-9]*)\"}", RegexOptions.Multiline);
        private readonly Regex _userInfoRegexAlt = new Regex("{\"name\":\"(?<name>[^\"]+)\",\"uid\":(?<uid>[0-9]*)}", RegexOptions.Multiline);
        private readonly Regex _userInfoRegex = new Regex("{\"uid\":(?<uid>[0-9]*),\"name\":\"(?<name>[^\"]+)\",\"pic_square\":\"(?<pic_square>[^\"]+)\"}", RegexOptions.Multiline);
        private readonly int _uidsToBatch = 10;
        private readonly double _prec;
        public FriendsList AllMyFriends;
        private readonly object _asyncLock;
        #endregion //#region VARIABLES

        public LoggedInUser(string token, int uid, int udisToBatch, double prec)
        {
            _token = token;
            _myUid = uid;
            _uidsToBatch = udisToBatch;
            _prec = prec;
            AllMyFriends = new FriendsList(_prec, _myUid);
            _asyncLock = new object();
        }

        public LoggedInUser(string token) : this(token, 0, 3, 0.01)
        {}

        public void RunClustering(out string errMsg)
        {
            errMsg = "All is good...";
            try
            {
                GetAllUserFriends();
                FillFiendsConnections();
                AllMyFriends.ClusterList();
            }
            catch (Exception e)
            {
               errMsg ="Error:"+e.Message;
               return;
            } 
        }

        public void GetAllUserFriends()
        {
            var res =
                QueryHelper.GetRes(
                    @"fql.query?query=SELECT%20uid,%20name,%20pic_square%20FROM%20user%20WHERE%20uid%20IN%20(SELECT%20uid2%20FROM%20friend%20WHERE%20uid1%20=%20me())&",
                    _token);
            if (res.Contains("Invalid OAuth 2.0 Access Token"))
            {
                throw new Exception("Please Change Your Access Token");
            }
            var matches = _userInfoRegex.Matches(res);
            if (matches.Count == 0)
                matches = _userInfoRegexAlt.Matches(res);

            if (matches.Count == 0)
                throw new Exception("You have no friends... Sorry");

            _numOfFriends = matches.Count;

            foreach (Match match in matches)
            {
                AllMyFriends.AddUser(
                    new FacebookUser(
                    match.Groups["name"].Value,
                    match.Groups["uid"].Value,
                    match.Groups["pic_square"].Value));
            }
        }

        public void FillFiendsConnections()
        {
            _asyncCounter = 0;
            var tenUids = "";
            
            const string reqFromat = @"fql.query?query=SELECT%20uid1,%20uid2%20FROM%20friend%20WHERE%20uid1%20IN%20({0})%20AND%20uid2%20IN%20(SELECT%20uid2%20FROM%20friend%20WHERE%20uid1=me())&";

            var i = 0;
            foreach (var friend in AllMyFriends.Values)
            {
                tenUids += String.Format("{0}", friend.Uid);

                if ((i + 1) % _uidsToBatch == 0 || i == _numOfFriends - 1)
                {
                    QueryHelper.GetResAsync(String.Format(reqFromat, tenUids), _token, i + 1, HandleBatchUidsFQL);
                    tenUids = String.Empty;
                }
                else
                {
                    tenUids += ",";
                }
                i++;
            }
            while (!_doneFillingConnections)
            {
                System.Threading.Thread.Sleep(500);
            }
        }

        private void HandleBatchUidsFQL(object data, string res)
        {
            var i = (int) data;
            var numOfHandledUids = i%_uidsToBatch == 0 ? _uidsToBatch : i%_uidsToBatch;
            var matches = _mutualFriendsRegex.Matches(res);

            foreach (Match match in matches)
            {
                var uid1 = match.Groups["uid1"].Value;
                var uid2 = match.Groups["uid2"].Value;

                var user1 = AllMyFriends.GetUserByUID(uid1);
                var user2 = AllMyFriends.GetUserByUID(uid2);

                if (user1 == null || user2 == null)
                    continue;

                user1.AddMutualFriend(user2);
                user2.AddMutualFriend(user1);
            }
            lock (_asyncLock)
            {
                _asyncCounter += numOfHandledUids;
                if (_asyncCounter == _numOfFriends)
                {
                    _doneFillingConnections = true;
                }
            }
        }

        public static long GetTimeStamp()
        {
            var ticks = DateTime.UtcNow.Ticks - DateTime.Parse("01/01/1970 00:00:00").Ticks;
            ticks /= 10000000; //Convert windows ticks to seconds
            return ticks;
        }

        public LinkedList<FriendsList> GetGroups()
        {
            return AllMyFriends.SubLists;
        }

        public FriendsList GetLonelyFriends()
        {
            return AllMyFriends.LonelyFriends;
        }
    }
    
}
